Skip to content

Conversation

admin-coderabbit
Copy link
Owner

@admin-coderabbit admin-coderabbit commented Feb 4, 2026

This pull request was automatically created by @coderabbitai/e2e-reviewer.

Batch created pull request.

Summary by CodeRabbit

  • New Features

    • Added assignment source tracking to improve visibility into where group assignments originate within your organization's integrations.
    • Implemented automatic prevention of infinite assignment sync loops between external integrations.
    • Enhanced assignment synchronization to maintain and propagate assignment source metadata through inbound and outbound workflows.
  • Tests

    • Added comprehensive test coverage for assignment source handling and sync cycle prevention logic.

@coderabbit-eval
Copy link

coderabbit-eval bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

The changes introduce AssignmentSource, a new data class for tracking assignment origins across integration sync flows. Methods throughout the assignment and integration sync pipeline are extended to accept and propagate this metadata, enabling cycle detection by comparing integration IDs. A new service module provides serialization and construction utilities.

Changes

Cohort / File(s) Summary
AssignmentSource Service
src/sentry/integrations/services/assignment_source.py
New immutable dataclass providing source tracking with from_integration(), to_dict(), and from_dict() methods for serialization and construction from Integration or RpcIntegration objects.
Integration Mixin Updates
src/sentry/integrations/mixins/issues.py
Extended should_sync() methods in both IssueBasicIntegration and IssueSyncIntegration to accept optional sync_source parameter; updated sync_status_outbound() signature to include assignment_source parameter for cycle prevention.
Sync Flow Integration
src/sentry/integrations/utils/sync.py, src/sentry/integrations/tasks/sync_assignee_outbound.py, src/sentry/models/groupassignee.py
Threaded assignment_source parameter through assignment and deassignment flows; updated sync_group_assignee_outbound() and sync task signatures to accept and propagate assignment source metadata; enhanced task payload to include serialized assignment source dictionary.
Tests
tests/sentry/integrations/services/test_assignment_source.py, tests/sentry/models/test_groupassignee.py
Added unit tests for AssignmentSource serialization methods; updated existing tests to pass assignment_source parameter and verify cycle-prevention behavior when source integration matches.

Sequence Diagram

sequenceDiagram
    participant Client
    participant GroupAssignee
    participant AssignmentSource
    participant SyncUtils
    participant SyncTask
    participant Integration

    Client->>GroupAssignee: assign(group, user, assignment_source)
    GroupAssignee->>SyncUtils: sync_group_assignee_outbound(group, user, assign=True, assignment_source)
    SyncUtils->>AssignmentSource: to_dict() [serialize source]
    SyncUtils->>SyncTask: sync_assignee_outbound(..., assignment_source_dict)
    SyncTask->>AssignmentSource: from_dict(assignment_source_dict) [deserialize]
    SyncTask->>Integration: should_sync(attribute, sync_source)
    Integration-->>SyncTask: skip if sync_source.integration_id matches
    SyncTask->>Integration: sync_assignee_outbound(..., assignment_source)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A new source of truth now springs to light,
Assignment origins tracked in flight,
No more circles in the sync so deep,
Metadata flows where assignments leap,
Integration IDs guard the gate,
Hops away — the cycles skip their fate! 🌟

🚥 Pre-merge checks | ✅ 1 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Automated Test: ecosystem-sync-integration-after' does not describe the actual changes made in the pull request, which involve adding assignment source tracking to integration sync mechanisms. Replace the title with a descriptive summary of the main changes, such as 'Add assignment source tracking to integration sync flows' or 'Implement AssignmentSource for sync cycle prevention'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ecosystem-sync-integration-after

Comment @coderabbit eval help to get the list of available commands and usage tips.

@coderabbit-eval coderabbit-eval bot closed this Feb 4, 2026
Copy link

@coderabbit-eval coderabbit-eval bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/sentry/integrations/mixins/issues.py (1)

410-422: ⚠️ Potential issue | 🟡 Minor

Fix ExampleIntegration.sync_status_outbound to accept the new parameter.

Most implementations (Jira, JiraServer, Vsts) use **kwargs and are compatible with the new assignment_source parameter. However, ExampleIntegration.sync_status_outbound at src/sentry/integrations/example/integration.py:161 lacks **kwargs and will not accept the new parameter, causing compatibility issues. Add **kwargs to its signature:

def sync_status_outbound(self, external_issue, is_resolved, project_id, **kwargs):
    pass
🤖 Fix all issues with AI agents
In `@src/sentry/integrations/services/assignment_source.py`:
- Around line 14-18: The dataclass AssignmentSource currently sets queued:
datetime = timezone.now() which evaluates at import time; change it to use a
default factory so each instance gets the current time at creation (e.g.,
replace the queued default with field(default_factory=timezone.now)), and import
dataclasses.field if not already imported to support this change.
🧹 Nitpick comments (1)
tests/sentry/integrations/services/test_assignment_source.py (1)

13-19: Typo in test method name: inalidinvalid.

-    def test_from_dict_inalid_data(self):
+    def test_from_dict_invalid_data(self):

Comment on lines +14 to +18
@dataclass(frozen=True)
class AssignmentSource:
source_name: str
integration_id: int
queued: datetime = timezone.now()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: timezone.now() is evaluated at class definition time, not instance creation.

All AssignmentSource instances created without an explicit queued value will share the same timestamp (the time the module was loaded). Use field(default_factory=...) instead.

🐛 Proposed fix
-from dataclasses import asdict, dataclass
+from dataclasses import asdict, dataclass, field
 from datetime import datetime
 from typing import TYPE_CHECKING, Any

 from django.utils import timezone

 if TYPE_CHECKING:
     from sentry.integrations.models import Integration
     from sentry.integrations.services.integration import RpcIntegration


 `@dataclass`(frozen=True)
 class AssignmentSource:
     source_name: str
     integration_id: int
-    queued: datetime = timezone.now()
+    queued: datetime = field(default_factory=timezone.now)
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 18-18: Do not perform function call timezone.now in dataclass defaults

(RUF009)

🤖 Prompt for AI Agents
In `@src/sentry/integrations/services/assignment_source.py` around lines 14 - 18,
The dataclass AssignmentSource currently sets queued: datetime = timezone.now()
which evaluates at import time; change it to use a default factory so each
instance gets the current time at creation (e.g., replace the queued default
with field(default_factory=timezone.now)), and import dataclasses.field if not
already imported to support this change.

Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant